package com.whz.governance.http.client;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.whz.generic.api.ApiResponseWrapper;
import com.whz.generic.json.JacksonUtils;
import com.whz.generic.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

import java.util.Optional;

/**
 * enable sentinel for spring mvc
 * <p>we should create bean  {@link UrlCleaner}</p>
 * <p>we should create bean  {@link BlockExceptionHandler}</p>
 * <p>we should create bean  {@link RequestOriginParser}</p>
 *
 * @author whz
 * @see com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration
 */
@Slf4j
@Configuration
public class SentinelFallBackConfiguration {

    /**
     * 通过origin url 生成请求resource name. sentinel将针对这个name限流(该name的QPS)
     *
     * @return UrlCleaner
     */
    @Bean
    public static Optional<UrlCleaner> urlCleanerOptional() {
        Optional<UrlCleaner> cleaner = getBean(UrlCleaner.class);
        if (cleaner.isPresent()) {
            return cleaner;
        } else {
            return Optional.of(originUrl -> {
                // 针对所有origin
                return "";
            });
        }
    }

    /**
     * sentinel 限流规则命中请求后,抛出{@link BlockException}异常,此方法统一处理.
     *
     * @return BlockExceptionHandler
     */
    @Bean
    public static Optional<BlockExceptionHandler> blockExceptionHandlerOptional() {
        Optional<BlockExceptionHandler> cleaner = getBean(BlockExceptionHandler.class);
        if (cleaner.isPresent()) {
            return cleaner;
        } else {
            return Optional.of((request, response, e) -> {
                if (!response.isCommitted()) {
                    response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
                    JacksonUtils.writeToOutput(response.getOutputStream(),
                                               ApiResponseWrapper.fail(HttpStatus.TOO_MANY_REQUESTS.name()));
                    response.getOutputStream().close();
                }
            });
        }
    }

    /**
     * 根据{@link javax.servlet.http.HttpServletRequest} 生成请求来源origin,该来源的请求将按照总QPS限流
     *
     * @return Optional<RequestOriginParser>
     */
    @Bean
    public static Optional<RequestOriginParser> requestOriginParserOptional() {
        Optional<RequestOriginParser> cleaner = getBean(RequestOriginParser.class);
        if (cleaner.isPresent()) {
            return cleaner;
        } else {
            return Optional.of(request -> "");
        }
    }

    private static <T> Optional<T> getBean(Class<T> clz) {
        return SpringUtils.getBean(clz);
    }
}